package com.sneakxy.cloudbase.platform.service;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.HostAuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAccount;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;

import com.sneakxy.cloudbase.platform.shiro.GetAuthorizationInfoRealm;
import com.sneakxy.cloudbase.platform.shiro.ShiroAccount;

/**
 * 
 * @author 潜行的青衣
 * @see
 */
public class ShiroRealm extends AuthorizingRealm implements GetAuthorizationInfoRealm {

	public static final String AUTHORIZATION_CACHE_KEY = "AUTHORIZATION_CACHE_KEY_";
	public static final String AUTHENTICATION_CACHE_KEY = "AUTHENTICATION_CACHE_KEY_";
	public static final String AUTHENTICATION_CACHE_SEPARATOR = "@";
	
	@Lazy
	@Autowired
	@Qualifier("shiroService")
	private IShiroService shiroService;
	
	public ShiroRealm() {
		this(null, null);
	}
	
	public ShiroRealm(CacheManager cacheManager) {
        this(cacheManager, null);
    }
	
	public ShiroRealm(CredentialsMatcher matcher) {
        this(null, matcher);
    }
	
	public ShiroRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
		super(cacheManager, matcher);
		this.setAuthenticationTokenClass(UsernamePasswordToken.class);
	}
	
	/* (non-Javadoc)
	 * 认证
	 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		String host = ((HostAuthenticationToken) token).getHost();
		ShiroAccount account = shiroService.findShiroAccount(token.getPrincipal(), host);
		if(account == null) {
			throw new UnknownAccountException("no such account");
		}
		if(account.getStatus() != ShiroAccount.STATUS_ACTIVE) {
			throw new LockedAccountException("account was locked"); 
		}
		return new SimpleAccount(account, account.getPassword(), this.getName());
	}

	/**
	 * 复权
	 */
	/* (non-Javadoc)
	 * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroAccount account = (ShiroAccount) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		//账户拥有角色、权限
		//角色
		List<String> roles = shiroService.findRoles(account);
		if(roles == null) {
			throw new UnauthenticatedException();
		}
		info.addRoles(roles);
		//其他角色、权限，例如：岗位（待补充）
		List<String> permissions = shiroService.findPermissions(account, roles);
		if(permissions == null) {
			throw new UnauthenticatedException();
		}
		info.addStringPermissions(permissions);
		return info;
	}

	@Override
	public AuthorizationInfo getCurrentAuthorizationInfo(PrincipalCollection principals) {
		return super.getAuthorizationInfo(principals);
	}

	public Object getAuthenticationCacheKey(String account, String host) {
		return StringUtils.join(AUTHENTICATION_CACHE_KEY, account, AUTHENTICATION_CACHE_SEPARATOR, host);
	}
	
	public void clearCachedAuthenticationInfo(String account, String host) {
		Object authenticationCacheKey = getAuthenticationCacheKey(account, host);
		getAuthenticationCache().remove(authenticationCacheKey);
	}
	
	/**
	 * 缓存保存用户信息使用，必须与getAvailablePrincipal保持一致
	 */
	@Override
	protected Object getAuthenticationCacheKey(AuthenticationToken token) {
		String host = ((HostAuthenticationToken) token).getHost();
		return getAuthenticationCacheKey((String) token.getPrincipal(), host);
	}

	/**
	 * 删除缓存用户信息使用，必须与getAuthenticationCacheKey保持一致
	 * @see org.apache.shiro.realm.CachingRealm#getAvailablePrincipal(org.apache.shiro.subject.PrincipalCollection)
	 */
	@Override
	protected Object getAvailablePrincipal(PrincipalCollection principals) {
		Object principal = principals.getPrimaryPrincipal();
		if(principal instanceof ShiroAccount) {
			ShiroAccount account = (ShiroAccount) principal;
			return getAuthenticationCacheKey(account.getAccount(), account.getHost());
		}
		return super.getAvailablePrincipal(principals);
	}
	
	/**
	 * 缓存权限信息使用
	 */
	@Override
	protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
		Object principal = principals.getPrimaryPrincipal();
		if(principal instanceof ShiroAccount) {
			return StringUtils.join(AUTHORIZATION_CACHE_KEY, ((ShiroAccount) principal).getId());
		}
		return super.getAuthorizationCacheKey(principals);
	}

	@Override
	protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
		return this.getPermissions(info).contains(permission);
	}

}
